import { addClass, removeClass, getUid, getZindex, setCss } from '../utils.js';

class PopoverEffect {
    trigger = 'click';
    reference = null;
    container = null;
    scrolling = false;
    classNames = ['an-popover'];
    // top | top-start | top-end
    // right | right-start | right-end
    // bottom | bottom-start | bottom-end
    // left | left-start | left-end
    placement = 'bottom';
    offset = 10;
    delay = 100;
    opened = null;
    closed = null;
    uid = '';
    constructor(reference, options) {
        this.reference = reference;
        const {
            classNames,
            styles,
            placement,
            zIndex,
            offset,
            content,
            delay,
            opened,
            closed,
        } = options;
        if (Array.isArray(classNames)) {
            this.classNames.push(...classNames.filter(v => typeof v === 'string'));
        }
        if (placement) {
            this.placement = placement;
        }
        if (offset) {
            this.offset = offset;
        }
        if (delay) {
            this.delay = delay;
        }
        if (opened) {
            this.opened = opened;
        }
        if (closed) {
            this.closed = closed;
        }
        this.uid = getUid();
        this.reference.dataset.popoverid = this.uid;
        this.create(content, styles, zIndex);
        this.mount();
    }
    create(content, styles, zIndex) {
        const { classNames, uid } = this;
        const el = document.createElement('div');
        el.dataset.popoverid = uid;
        addClass(el, classNames);
        setCss(el, styles, {
            'z-index': zIndex || getZindex()
        });
        if (content instanceof Element) {
            el.appendChild(content);
        } else {
            el.innerHTML = content;
        }
        this.container = el;
    }
    updatePosition() {
        const { offset } = this;
        const ww = window.innerWidth;
        const wh = window.innerHeight;
        const referenceRect = this.reference.getBoundingClientRect();
        const contentRect = this.container.getBoundingClientRect();
        const contentW = contentRect.width;
        const contentH = contentRect.height;
        const offsetContentW = contentW + offset;
        const offsetContentH = contentH + offset;
        const placements = [];
        if (referenceRect.top > offsetContentH) {
            const top = referenceRect.top - offsetContentH;
            if (ww - referenceRect.left > contentW) {
                placements.push({
                    placement: 'top-start',
                    css: {
                        top,
                        left: referenceRect.left,
                    }
                });
            }
            if (referenceRect.width / 2 + referenceRect.left > contentW / 2
                && ww - (referenceRect.width / 2 + referenceRect.left > contentW / 2)) {
                placements.push({
                    placement: 'top',
                    css: {
                        top,
                        left: (referenceRect.left + referenceRect.width / 2) - contentW / 2,
                    }
                });
            }
            if (referenceRect.right > contentW) {
                placements.push({
                    placement: 'top-end',
                    css: {
                        top,
                        left: referenceRect.right - contentW
                    }
                });
            }
        }

        if (ww - referenceRect.right > offsetContentW) {
            const left = referenceRect.right + offset;
            if (wh - referenceRect.top > contentH) {
                placements.push({
                    placement: 'right-start',
                    css: {
                        top: referenceRect.top,
                        left,
                    }
                });
            }
            if (referenceRect.height / 2 + referenceRect.top > contentH / 2
                && wh - (referenceRect.height / 2 + referenceRect.top) > contentH / 2) {
                placements.push({
                    placement: 'right',
                    css: {
                        top: referenceRect.height / 2 + referenceRect.top - contentH / 2,
                        left,
                    }
                });
            }
            if (referenceRect.bottom > contentH) {
                placements.push({
                    placement: 'right-end',
                    css: {
                        top: referenceRect.bottom - contentH,
                        left,
                    }
                });
            }
        }

        if (wh - referenceRect.bottom > offsetContentH) {
            const top = referenceRect.bottom + offset;
            if (ww - referenceRect.left > contentW) {
                placements.push({
                    placement: 'bottom-start',
                    css: {
                        top,
                        left: referenceRect.left,
                    }
                });
            }
            if (referenceRect.width / 2 + referenceRect.left > contentW / 2
                && ww - (referenceRect.width / 2 + referenceRect.left > contentW / 2)) {
                placements.push({
                    placement: 'bottom',
                    css: {
                        top,
                        left: (referenceRect.left + referenceRect.width / 2) - contentW / 2,
                    }
                });
            }
            if (referenceRect.right > contentW) {
                placements.push({
                    placement: 'bottom-end',
                    css: {
                        top,
                        left: referenceRect.right - contentW,
                    }
                });
            }
        }

        if (referenceRect.left > offsetContentW) {
            const left = referenceRect.left - offsetContentW;
            if (wh - referenceRect.top > contentH) {
                placements.push({
                    placement: 'left-start',
                    css: {
                        top: referenceRect.top,
                        left,
                    }
                });
            }
            if (referenceRect.height / 2 + referenceRect.top > contentH / 2
                && wh - (referenceRect.height / 2 + referenceRect.top) > contentH / 2) {
                placements.push({
                    placement: 'left',
                    css: {
                        top: referenceRect.height / 2 + referenceRect.top - contentH / 2,
                        left,
                    }
                });
            }
            if (referenceRect.bottom > contentH) {
                placements.push({
                    placement: 'left-end',
                    css: {
                        top: referenceRect.bottom - contentH,
                        left,
                    }
                });
            }
        }
        const last = placements.find(o => o.placement === this.placement) || placements[0];
        this.container.dataset.placement = last.placement;
        for (const key in last.css) {
            if (['top', 'left', 'right', 'bottom'].includes(key)) {
                last.css[key] = `${last.css[key]}px`
            }
        }
        setCss(this.container, last.css);
    }
    isShow() {
        return this.container.style.display === 'block';
    }
    show() {
        setCss(this.container, { display: 'block' });
        this.updatePosition();
        addClass(this.container, ['an-popover-enter']);
        if (typeof this.opened === 'function') {
            this.opened();
        }
    }
    hide() {
        removeClass(this.container, ['an-popover-enter']);
        this.hideAnimate();
    }
    toggel() {
        if (this.isShow()) {
            this.hide();
        } else {
            this.show();
        }
    }
    hideAnimate() {
        const fn = () => {
            setCss(this.container, { display: 'none' });
            this.scrolling = false;
            this.container.removeEventListener('transitionend', fn);
            if (typeof this.closed === 'function') {
                this.closed();
            }
        }
        this.container.addEventListener('transitionend', fn)
    }
    mount() {
        document.body.appendChild(this.container);
    }
    isHide(e) {
        const tar = e.target;
        if (
            this.reference === tar
            || this.reference.contains(tar)
            || this.container === tar
            || this.container.contains(tar)
        ) {
            return;
        }
        this.hide();
    }
    scrollHide() {
        if (this.scrolling) {
            return;
        }
        this.scrolling = true;
        this.hide();
    }
    destroy() {
        this.container && this.container.remove();
        if (typeof this.destroyed === 'function') {
            this.destroyed();
        }
    }
}
const popoverCaches = new Map();
document.addEventListener('click', (e) => {
    for (const [i, inst] of popoverCaches) {
        if (inst.isShow()) {
            inst.isHide(e);
        }
    }
});
window.addEventListener('scroll', (e) => {
    for (const [i, inst] of popoverCaches) {
        if (inst.isShow()) {
            inst.scrollHide(e);
        }
    }
});
export const popover = {
    open(reference, options) {
        if (!reference instanceof Element) {
            reference = document.querySelector(reference);
        }
        let inst = popoverCaches.get(reference.dataset.popoverid);
        if (inst) {
            return inst;
        }
        inst = new PopoverEffect(reference, options);
        popoverCaches.set(inst.uid, inst);
        return inst;
    }
}